package org.abio.XasML.reflect
{
	import flash.system.ApplicationDomain;
	import flash.utils.Dictionary;
	import flash.utils.describeType;
	
	import org.abio.XasML.converter.object2Class;
	
	public class ClassInfo
	{
		private var Xml:XML=null;
		public function get xml():XML{	return this.Xml;}
		private var Defination:Class=null;
		public function get defination():Class{	return this.Defination;}
		private var Domain:ApplicationDomain=null;
		public function get domain():ApplicationDomain{	return this.Domain;}
		
		private var elementDict:Dictionary=new Dictionary();
		
		internal var name:String=null;
		public function get Name():String{	return this.name;}
		public function get Shortname():String{	return this.name.split('::').reverse()[0];}
		internal var base:String=null;
		internal var isDynamic:String=null;
		internal var isFinal:String=null;
		internal var isStatic:String=null;
		
		internal var extendsClass:Array=new Array();
		internal var implementsInterface:Array=new Array();
		internal var factory:Array=new Array();
		internal var method:Array=new Array();
		internal var variable:Array=new Array();
		internal var accessor:Array=new Array();
		
		static public function of(type:*, domain:ApplicationDomain=null):ClassInfo{
			return new ClassInfo(type, domain);
		}

		public function ClassInfo(type:*, domain:ApplicationDomain=null)
		{
			this.Domain=(domain==null ? ApplicationDomain.currentDomain : domain);
			this.Defination=object2Class(type, this.domain);
			this.Xml=describeType(this.defination);

			this.elementDict['extendsClass']=Base;
			this.elementDict['implementsInterface']=Base;
			this.elementDict['factory']=Factory;
			this.elementDict['constructor']=ConstructorInfo;
			this.elementDict['method']=MethodInfo;
			this.elementDict['parameter']=ParameterInfo;
			this.elementDict['variable']=PropertyInfo;
			this.elementDict['accessor']=PropertyInfo;
			this.elementDict['metadata']=MetadataInfo;
			this.elementDict['arg']=ArgInfo;
			
			this._xml_element(this, this.xml);
			
			var THIS:ClassInfo=this;
			function for_each_set_owner(element:*, index:int, arr:Array):void{	element._owner=THIS;}
			function for_each_set_static(element:*, index:int, arr:Array):void{	element._bStatic=true;}
			this.method.forEach(for_each_set_owner);
			this.method.forEach(for_each_set_static);
			this.variable.forEach(for_each_set_owner);
			this.variable.forEach(for_each_set_static);
			this.accessor.forEach(for_each_set_owner);
			this.accessor.forEach(for_each_set_static);
			this.factory[0].method.forEach(for_each_set_owner);
			this.factory[0].variable.forEach(for_each_set_owner);
			this.factory[0].accessor.forEach(for_each_set_owner);
			if(this.factory[0].constructor[0]==null){	this.factory[0].constructor[0]=new ConstructorInfo();}
			this.factory[0].constructor.forEach(for_each_set_owner);
		}
		
		private function _xml_element(obj:Object, xml:XML):void{
			for each(var attr:XML in xml.attributes()){
				obj[attr.name().toString()]=attr;
			}
			for each(var child:XML in xml.children()){
				var tempObj:Object=new this.elementDict[child.name().toString()]();
				this._xml_element(tempObj, child);
				(obj[child.name().toString()] as Array).push(tempObj);
			}
		}
		
		public function isType(type:Class):Boolean{
			if(this.defination==type){
				return true;
			}else{
				return (this.factory[0] as Factory).isType(type);
			}
		}
		
		public function get constructor():ConstructorInfo{
			return (this.factory[0] as Factory).constructor[0] as ConstructorInfo;
		}
		
		public function get staticMethods():Array{	return this.method;}
		public function get staticProperties():Array{	return this.variable.concat(this.accessor);}
		public function get methods():Array{	return (this.factory[0] as Factory).methods;}
		public function get properties():Array{	return (this.factory[0] as Factory).properties;}
		public function get metadatas():Array{	return (this.factory[0] as Factory).metadatas;}

		private function _findByName(arr:Array, name:String):Object{
			return arr.filter(function filter(element:*, index:int, arr:Array):Boolean{
				return element.Name==name;
			})[0];
		}
		public function getStaticMethod(name:String):MethodInfo{	return this._findByName(this.staticMethods, name) as MethodInfo;}
		public function getStaticProperty(name:String):PropertyInfo{	return this._findByName(this.staticProperties, name) as PropertyInfo;}
		public function getMethod(name:String):MethodInfo{	return this._findByName(this.methods, name) as MethodInfo;}
		public function getProperty(name:String):PropertyInfo{	return this._findByName(this.properties, name) as PropertyInfo;}
		public function getMetadata(name:String):Object{	return (this.factory[0] as Factory).getMetadata(name);}
	}
}